/*
 * Copyright 2025 LinQingYing. and contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * The use of this source code is governed by the Apache License 2.0,
 * which allows users to freely use, modify, and distribute the code,
 * provided they adhere to the terms of the license.
 *
 * The software is provided "as-is", and the authors are not responsible for
 * any damages or issues arising from its use.
 *
 */

package org.cangnova.cangjie.debugger.dap.ui

import com.intellij.execution.ui.ConsoleViewContentType
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.xdebugger.XDebugProcess
import com.intellij.xdebugger.XDebugSession
import com.intellij.xdebugger.XSourcePosition
import com.intellij.xdebugger.breakpoints.XBreakpointHandler
import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider
import com.intellij.xdebugger.frame.XSuspendContext
import kotlinx.coroutines.*
import org.cangnova.cangjie.debugger.RunParameters
import org.cangnova.cangjie.debugger.dap.core.AdapterEvent
import org.cangnova.cangjie.debugger.dap.core.LaunchArguments
import org.cangnova.cangjie.debugger.dap.provider.DapDebuggerProvider
import org.cangnova.cangjie.debugger.dap.server.PortStrategy
import org.cangnova.cangjie.debugger.dap.server.ServerConfig
import org.cangnova.cangjie.debugger.dap.session.*
import kotlin.io.path.absolutePathString

/**
 * 仓颉调试进程（单服务器模式）
 *
 * 每个调试进程维护一个调试会话，通过DebugSessionCoordinator管理。
 * 由于协调器采用单服务器模式，同一项目下只能有一个活跃的调试会话。
 *
 * 正确的DAP启动流程:
 * 1. 构造函数: 启动服务器、建立连接、发送initialize、接收initialized响应
 * 2. startInitialization(): 发送launch命令
 * 3. sessionInitialized(): 发送configurationDone（此时断点已通过BreakpointHandler注册）
 */
class DapDebugProcess(
    private val runConfig: RunParameters,
    session: XDebugSession
) : XDebugProcess(session) {

    companion object {
        private val LOG = Logger.getInstance(DapDebugProcess::class.java)
    }

    // 协程作用域 - 绑定到进程生命周期
    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)

    // 会话协调器
    private val coordinator: DebugSessionCoordinator by lazy {
        DefaultDebugSessionCoordinator.getInstance(session.project)
    }

    // 托管的调试会话
    private var managedSession: ManagedDebugSession? = null
    private var sessionId: String? = null

    // UI组件
    private val editorsProvider = CangJieDebuggerEditorsProvider()
    private val breakpointHandler: CangJieBreakpointHandler = CangJieBreakpointHandler(this@DapDebugProcess)

    // 初始化状态
    @Volatile
    private var initialized = false // initialize 已完成

    @Volatile
    private var launched = false // launch 已完成

    // initialize完成的 Deferred
    private val initializeDeferred = CompletableDeferred<Unit>()

    // launch完成的 Deferred
    private val launchDeferred = CompletableDeferred<Unit>()

    init {
        // 在构造函数中同步执行服务器连接和initialize
        runBlocking {
            try {
                // 第一步：启动服务器和建立连接
                createServerAndConnection()

                // 第二步：发送 initialize 并等待响应
                sendInitialize()

                initializeDeferred.complete(Unit)
            } catch (e: Exception) {
                LOG.error("Failed to initialize in constructor", e)
                initializeDeferred.completeExceptionally(e)
                // 在EDT上报告错误
                ApplicationManager.getApplication().invokeLater {
                    session.reportError("Failed to start debugger: ${e.message}")
                    session.stop()
                }
                throw e
            }
        }
    }

    /**
     * 第一步：创建服务器和连接
     */
    private suspend fun createServerAndConnection() = withContext(Dispatchers.IO) {
        try {
            LOG.info("Creating server and connection")

            // 创建会话配置
            val sessionConfig = createSessionConfig()

            // 使用协调器创建会话（启动服务器、建立连接、初始化adapter）
            val result = coordinator.createSession(sessionConfig)

            if (result.isFailure) {
                throw result.exceptionOrNull()
                    ?: IllegalStateException("Session creation failed")
            }

            sessionId = result.getOrThrow()
            managedSession = coordinator.getSession(sessionId!!)
                ?: throw IllegalStateException("Session not found after creation")

            LOG.info("Debug session created: $sessionId")

            // 订阅会话事件
            managedSession!!.subscribeEvents { event ->
                handleDebugEvent(event)
            }

            run {
            session.consoleView?.print(
                "Debug server started and connected\n",
                ConsoleViewContentType.SYSTEM_OUTPUT
            )
            }

            LOG.info("Server and connection created successfully")
        } catch (e: Exception) {
            LOG.error("Failed to create server and connection", e)
            throw e
        }
    }

    /**
     * 第二步：发送 initialize 命令并等待 initialized 响应
     */
    private suspend fun sendInitialize() = withContext(Dispatchers.IO) {
        try {
            LOG.info("Sending initialize request")

            val debugSession = requireSession()

            // 发送initialize请求
            val capabilities = debugSession.adapter.initialize()
                .getOrElse { error ->
                    LOG.error("Initialize request failed", error)
                    run{
                        session.reportError("Failed to initialize: ${error.message}")
                        session.stop()
                    }

                    throw error
                }

            LOG.info("Initialize request sent, capabilities: $capabilities")





           run {
                session.consoleView?.print(
                    "Debugger initialized\n",
                    ConsoleViewContentType.SYSTEM_OUTPUT
                )
            }

        } catch (e: TimeoutCancellationException) {
            LOG.error("Initialize timeout", e)
            run {
                session.reportError("Debugger initialization timeout")
                session.stop()
            }
            throw e
        } catch (e: Exception) {
            LOG.error("Initialize failed", e)
            throw e
        }
    }

    /**
     * 发送 launch 命令（在构造完成后调用）
     */
    fun startInitialization() {
        LOG.info("Starting launch phase")

        ApplicationManager.getApplication().executeOnPooledThread {
            scope.launch {
                try {
                    // 等待 initialize 完成
                    initializeDeferred.await()

                    LOG.info("Sending launch request")

                    val debugSession = requireSession()

                    // 发送launch请求
                    debugSession.adapter.launch(createSessionConfig().launchArguments).getOrElse { error ->
                        LOG.error("Launch request failed", error)
                        run {
                            session.reportError("Failed to launch: ${error.message}")
                            session.stop()
                        }
                        throw error
                    }

                    LOG.info("Launch request sent, waiting for program to start")

                    launched = true
                    launchDeferred.complete(Unit)

                    run {
                        session.consoleView?.print(
                            "Program launched\n",
                            ConsoleViewContentType.SYSTEM_OUTPUT
                        )
                    }

                } catch (e: Exception) {
                    LOG.error("Launch failed", e)
                    launchDeferred.completeExceptionally(e)
                    run {
                        session.reportError("Failed to launch program: ${e.message}")
                        session.stop()
                    }
                }
            }
        }
    }

    /**
     * 会话初始化完成（由IntelliJ框架调用）
     * 此时断点已通过BreakpointHandler注册，发送configurationDone
     */
    override fun sessionInitialized() {
        LOG.info("sessionInitialized called - sending configurationDone")

        ApplicationManager.getApplication().executeOnPooledThread {
            scope.launch {
                try {
                    // 等待 launch 完成
                    launchDeferred.await()

                    LOG.info("Launch completed, sending configurationDone")

                    val debugSession = requireSession()

                    // 发送configurationDone（此时断点已通过BreakpointHandler注册）
                    debugSession.adapter.configurationDone()
                        .onSuccess {
                            LOG.info("configurationDone sent successfully, debugging started")
                            run  {
                                this@DapDebugProcess.session.consoleView?.print(
                                    "Debugging session ready\n",
                                    ConsoleViewContentType.SYSTEM_OUTPUT
                                )
                            }
                        }
                        .onFailure { error ->
                            LOG.error("Failed to send configurationDone", error)
                            run {
                                this@DapDebugProcess.session.reportError("Failed to complete debugger configuration: ${error.message}")
                            }
                        }
                } catch (e: Exception) {
                    LOG.error("Error in sessionInitialized", e)
                    run{
                        this@DapDebugProcess.session.reportError("Debugger initialization failed: ${e.message}")
                        this@DapDebugProcess.session.stop()
                    }
                }
            }
        }
    }

    /**
     * 创建会话配置
     */
    private fun createSessionConfig(): DebugSessionConfig {
        // 使用从 RunParameters 传递的 DebuggerProvider
        val debuggerProvider = runConfig.debuggerProvider

        return DebugSessionConfig(
            project = session.project,
            debuggerProvider = debuggerProvider,
            serverConfig = ServerConfig(
                executablePath = "", // 留空，DefaultServerLifecycleManager 会使用 debuggerProvider
                debuggerType = debuggerProvider.debuggerType,
                portStrategy = PortStrategy.AutoAllocate,
                startupTimeoutMs = 10000
            ),
            launchArguments = LaunchArguments(
                program = runConfig.runExecutable.exePath,
                arguments = runConfig.runExecutable.parametersList.parameters,
                workingDirectory = runConfig.runExecutable.workingDirectory?.absolutePathString() ?: "",
                environment = runConfig.runExecutable.environment
            ),
            timeouts = SessionTimeouts(
                initializationTimeoutMs = 10000,
                launchTimeoutMs = 30000,
                operationTimeoutMs = 5000,
                stopTimeoutMs = 5000
            )
        )
    }

    /**
     * 处理调试事件
     */
    private fun handleDebugEvent(event: AdapterEvent) {
        scope.launch {
            try {
                when (event) {
                    is AdapterEvent.Initialized -> {
                        LOG.info("Received initialized event from DAP server")
                        initialized = true
                    }

                    is AdapterEvent.Stopped -> {
                        LOG.info("Program stopped: ${event.reason}")

                        // 获取线程列表和调用堆栈
                        val debugSession = managedSession ?: return@launch

                        val threads = debugSession.adapter.getThreads().getOrNull() ?: emptyList()
                        val frames = debugSession.adapter.getStackTrace(event.threadId).getOrNull() ?: emptyList()

                        run{
                            val suspendContext = CangJieSuspendContext(
                                debugProcess = this@DapDebugProcess,
                                activeThreadId = event.threadId,
                                threads = threads,
                                frames = frames
                            )
                            this@DapDebugProcess.session.positionReached(suspendContext)
                        }
                    }

                    is AdapterEvent.Continued -> {
                        LOG.info("Program continued")
                    }

                    is AdapterEvent.Terminated -> {
                        LOG.info("Program terminated")
                        run {
                            session.stop()
                        }
                    }

                    is AdapterEvent.Exited -> {
                        LOG.info("Program exited with code ${event.exitCode}")
                        run {
                            session.consoleView?.print(
                                "\nProcess exited with code ${event.exitCode}\n",
                                ConsoleViewContentType.SYSTEM_OUTPUT
                            )
                            session.stop()
                        }
                    }

                    is AdapterEvent.Output -> {
                        run{
                            val contentType = when (event.category) {
                                "stderr" -> ConsoleViewContentType.ERROR_OUTPUT
                                "console" -> ConsoleViewContentType.NORMAL_OUTPUT
                                else -> ConsoleViewContentType.SYSTEM_OUTPUT
                            }
                            session.consoleView?.print(event.output, contentType)
                        }
                    }

                    is AdapterEvent.BreakpointChanged -> {
                        LOG.debug("Breakpoint changed: ${event.breakpoint}")
                    }

                    else -> {
                        LOG.debug("Unhandled event: $event")
                    }
                }
            } catch (e: Exception) {
                LOG.error("Error handling debug event", e)
            }
        }
    }

    // ==================== XDebugProcess 接口实现 ====================

    override fun getEditorsProvider(): XDebuggerEditorsProvider {
        return editorsProvider
    }

    override fun getBreakpointHandlers(): Array<XBreakpointHandler<*>> {

        return arrayOf(breakpointHandler)
    }

    override fun resume(context: XSuspendContext?) {
        executeAsync("resume") {
            val session = requireSession()
            val threadId = (context as? CangJieSuspendContext)?.activeThreadId

            session.resume(threadId).onFailure { error ->
                reportError("Failed to resume: ${error.message}")
            }
        }
    }

    override fun startStepOver(context: XSuspendContext?) {
        executeAsync("step over") {
            val session = requireSession()
            val threadId = (context as? CangJieSuspendContext)?.activeThreadId
                ?: return@executeAsync

            session.stepOver(threadId).onFailure { error ->
                reportError("Failed to step over: ${error.message}")
            }
        }
    }

    override fun startStepInto(context: XSuspendContext?) {
        executeAsync("step into") {
            val session = requireSession()
            val threadId = (context as? CangJieSuspendContext)?.activeThreadId
                ?: return@executeAsync

            session.stepInto(threadId).onFailure { error ->
                reportError("Failed to step into: ${error.message}")
            }
        }
    }

    override fun startStepOut(context: XSuspendContext?) {
        executeAsync("step out") {
            val session = requireSession()
            val threadId = (context as? CangJieSuspendContext)?.activeThreadId
                ?: return@executeAsync

            session.stepOut(threadId).onFailure { error ->
                reportError("Failed to step out: ${error.message}")
            }
        }
    }

    override fun stop() {
        LOG.info("Stopping debug process")

        // 异步停止,不阻塞EDT
        ApplicationManager.getApplication().executeOnPooledThread {
            scope.launch {
                try {
                    sessionId?.let { id ->
                        coordinator.destroySession(id, timeoutMs = 5000)
                    }

                    // 取消所有协程
                    scope.cancel()

                    LOG.info("Debug process stopped")
                } catch (e: Exception) {
                    LOG.error("Error stopping debug process", e)
                }
            }
        }
    }

    override fun runToPosition(position: XSourcePosition, context: XSuspendContext?) {
        // TODO: 实现运行到光标位置
        LOG.warn("runToPosition not implemented yet")
    }

    // ==================== 辅助方法 ====================

    /**
     * 执行异步操作（不阻塞EDT）
     */
    private fun executeAsync(operationName: String, operation: suspend () -> Unit) {
        if (!initialized || !launched) {
            LOG.warn("Cannot $operationName: debug process not fully initialized")
            return
        }

        ApplicationManager.getApplication().executeOnPooledThread {
            scope.launch {
                try {
                    operation()
                } catch (e: CancellationException) {
                    LOG.debug("$operationName cancelled")
                } catch (e: Exception) {
                    LOG.error("Error during $operationName", e)
                    reportError("$operationName failed: ${e.message}")
                }
            }
        }
    }

    /**
     * 获取会话（如果未初始化则抛出异常）
     */
    private fun requireSession(): ManagedDebugSession {
        return managedSession
            ?: throw IllegalStateException("Debug session not initialized")
    }

    /**
     * 报告错误到UI（在EDT上）
     */
    private fun reportError(message: String) {
        ApplicationManager.getApplication().invokeLater {
            session.reportError(message)
        }
    }

    // ==================== 公共API ====================

    /**
     * 获取托管会话（用于UI组件）
     */
    fun getManagedSession(): ManagedDebugSession? = managedSession

    /**
     * 检查是否已初始化
     */
    fun isInitialized(): Boolean = initialized && launched
}


